package com.hanhai.zrb.api.biz.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hanhai.zrb.api.biz.dao.mybatis.UserFollowDao;
import com.hanhai.zrb.api.biz.service.UserFollowService;
import com.hanhai.zrb.api.biz.service.UserService;
import com.hanhai.zrb.api.util.UserUtil;
import com.hanhai.zrb.model.user.User;
import com.hanhai.zrb.model.user.UserFollow;
import com.hanhai.zrb.model.user.enums.UserFollowRelationEnum;
import com.hanhai.zrb.model.user.enums.UserFollowStatusEnum;

@Service
public class UserFollowServiceImpl implements UserFollowService {

	private static final Date DEFAULT_CREATE_TIME = new Date();

	private static final int DEFAULT_LIMIT = 10;
	private Logger logger = Logger.getLogger(getClass());
	@Resource
	private UserFollowDao userFollowDao;

	@Resource
	private UserService userService;

	@Override
	public void follow(UserFollow follow) {
		check(follow);
		Long fromUserId = follow.getFromUserId();
		Long toUserId = follow.getToUserId();
        if(fromUserId==null||toUserId==null){
        	logger.error("The fromUserId or toUserId is null when follow");
        	return;
        }
        if(fromUserId.equals(toUserId)){
        	logger.error("The fromUserId is equals toUserId when follow~No necessary~");
        	return;
        }
		UserFollow dbFollow = userFollowDao.getByFromAndToForUpdate(fromUserId,
				toUserId);
		// 重复关注
		Integer normalCode = UserFollowStatusEnum.NORMAL.getCode();
		if (dbFollow != null
				&& dbFollow.getStatus().equals(
						normalCode)) {
			logger.error("Cannot follow 2 times,fromUserId=" + fromUserId
					+ ",toUserId=" + toUserId);
			return;
		}
		if (dbFollow == null) {
			userFollowDao.follow(follow);
		} else {
			userFollowDao.updateStatus(dbFollow.getId(), normalCode);
		}
	}

	@Override
	public void unfollow(UserFollow follow) {
		check(follow);
		Long fromUserId = follow.getFromUserId();
		Long toUserId = follow.getToUserId();
	    if(fromUserId==null||toUserId==null){
        	logger.error("The fromUserId or toUserId is null when unfollow");
        	return;
        }
        if(fromUserId.equals(toUserId)){
        	logger.error("The fromUserId is equals toUserId when unfollow ~No necessary~");
        	return;
        }
		UserFollow dbFollow = userFollowDao.getByFromAndToForUpdate(fromUserId,
				toUserId);
		//不存在对应的“关注记录”
		if (dbFollow == null ) {
			logger.error("Cannot unfollow when not follow yet,fromUserId="
					+ fromUserId + ",toUserId=" + toUserId);
			return;
		}
		// "本来就是未关注"
		Integer removedCode = UserFollowStatusEnum.REMOVED.getCode();
		if(dbFollow.getStatus().equals(removedCode)){
			logger.error("Cannot unfollow when has unfollowed yet,fromUserId="
					+ fromUserId + ",toUserId=" + toUserId);
			return;
		}
		userFollowDao.unfollow(follow);
	}

	private void check(UserFollow follow) {
		if (follow == null || follow.getFromUserId() == null
				|| follow.getToUserId() == null) {
			throw new RuntimeException(
					"The follow is null or fromUserId-toUserId is null");
		}
	}

	// 判断2个人之间的关系，只需要2次查询
	// 判断1个人和多个人之间的关系，至少需要2次查询。如果知道这多个人已经是“自己关注的”或者
	// "关注自己的"，那么只需要1次查询
	public Integer relation(Long fromUserId, Long toUserId) {
		UserFollow fromFollow = userFollowDao.getByFromAndTo(fromUserId,
				toUserId);
		UserFollow toFollow = userFollowDao
				.getByFromAndTo(toUserId, fromUserId);
		// 默认为“互不关注”
		Integer relation = UserFollowRelationEnum.NONE.getCode();
		boolean isFrom = fromFollow != null
				&& fromFollow.getStatus().equals(
						UserFollowStatusEnum.NORMAL.getCode());
		boolean isTo = toFollow != null
				&& toFollow.getStatus().equals(
						UserFollowStatusEnum.NORMAL.getCode());
		if (isFrom) {
			relation = UserFollowRelationEnum.FROM.getCode();
		}
		if (isTo) {
			relation = UserFollowRelationEnum.TO.getCode();
		}
		if (isFrom && isTo) {
			relation = UserFollowRelationEnum.EACH.getCode();
		}
		return relation;

	}

	@Override
	public List<UserFollow> followingListByUpdateTime(Long fromUserId,
			Date updateTime, Integer limit) {
		if (updateTime == null) {
			updateTime = DEFAULT_CREATE_TIME;
		}
		if (limit == null || limit <= 0 || limit > 50) {
			limit = DEFAULT_LIMIT;
		}
		List<UserFollow> followingList = userFollowDao
				.followingListByUpdateTime(fromUserId, updateTime, limit);
		fillToUser(followingList);
		fillRelationWhenFollowing(fromUserId, followingList);
		return followingList;
	}

	private void fillRelationWhenFollowing(Long fromUserId,
			List<UserFollow> followingList) {
		if (CollectionUtils.isNotEmpty(followingList)) {
			for (UserFollow follow : followingList) {
				List<Long> followedList = userFollowDao
						.followedListAllFromUserId(fromUserId);
				// 自己肯定已经关注了toUserId
				Integer relation = UserFollowRelationEnum.FROM.getCode();
				// 自己是否被toUserId关注
				boolean followed = exist(follow.getToUserId(), followedList);
				if (followed) {
					relation = UserFollowRelationEnum.EACH.getCode();
				}
				follow.setRelation(relation);
			}
		}
	}

	private boolean exist(Long userId, List<Long> followedList) {
		if (CollectionUtils.isEmpty(followedList)) {
			return false;
		}
		boolean exist = followedList.contains(userId);
		return exist;
	}

	private void fillToUser(List<UserFollow> list) {
		List<Long> toUserIdList = toUserIdList(list);
		if (toUserIdList != null && toUserIdList.size() > 0) {
			List<User> toUserList = userService.findByUid(toUserIdList);
			Map<Long, User> toUserMap = UserUtil.userListToMap(toUserList);
			for (UserFollow follow : list) {
				follow.setToUser(toUserMap.get(follow.getToUserId()));
			}
		}
	}

	private List<Long> toUserIdList(List<UserFollow> list) {
		if (list == null) {
			return null;
		}
		List<Long> toUserIdList = new ArrayList<Long>();
		for (UserFollow follow : list) {
			toUserIdList.add(follow.getToUserId());
		}
		return toUserIdList;
	}

	@Override
	public List<UserFollow> followedListByUpdateTime(Long toUserId,
			Date updateTime, Integer limit) {
		if (updateTime == null) {
			updateTime = DEFAULT_CREATE_TIME;
		}
		if (limit == null || limit <= 0 || limit > 50) {
			limit = 10;
		}
		List<UserFollow> followedList = userFollowDao.followedListByUpdateTime(
				toUserId, updateTime, limit);
		fillFromUser(followedList);
		fillRelationWhenFollowed(toUserId, followedList);
		return followedList;
	}

	private void fillRelationWhenFollowed(Long toUserId,
			List<UserFollow> followedList) {
		if (CollectionUtils.isNotEmpty(followedList)) {
			for (UserFollow follow : followedList) {
				List<Long> followingUserIdList = userFollowDao
						.followingListAllToUserId(toUserId);
				// 自己肯定已经被关注了
				Integer relation = UserFollowRelationEnum.TO.getCode();
				// 自己是否关注了他
				boolean followed = exist(follow.getFromUserId(),
						followingUserIdList);
				if (followed) {
					relation = UserFollowRelationEnum.EACH.getCode();
				}
				follow.setRelation(relation);
			}
		}
	}

	private void fillFromUser(List<UserFollow> list) {
		List<Long> fromUserIdList = fromUserIdList(list);
		if (fromUserIdList != null && fromUserIdList.size() > 0) {
			List<User> fromUserList = userService.findByUid(fromUserIdList);
			Map<Long, User> fromUserMap = UserUtil.userListToMap(fromUserList);
			for (UserFollow follow : list) {
				follow.setFromUser(fromUserMap.get(follow.getFromUserId()));
			}
		}
	}

	private List<Long> fromUserIdList(List<UserFollow> list) {
		if (list == null) {
			return null;
		}
		List<Long> fromUserIdList = new ArrayList<Long>();
		for (UserFollow follow : list) {
			fromUserIdList.add(follow.getFromUserId());
		}
		return fromUserIdList;
	}

	@Override
	public PageInfo followingListPage(Long fromUserId, Integer page,
			Integer rows) {
		PageHelper.startPage(page, rows);
		List<UserFollow> followingList = userFollowDao
				.followingListPage(fromUserId);
		fillToUser(followingList);
		fillRelationWhenFollowing(fromUserId, followingList);
		PageInfo pageInfo = new PageInfo(followingList);
		return pageInfo;
	}

	@Override
	public PageInfo followedListPage(Long toUserId, Integer page, Integer rows) {
		PageHelper.startPage(page, rows);
		List<UserFollow> followedList = userFollowDao
				.followedListPage(toUserId);
		fillFromUser(followedList);
		fillRelationWhenFollowed(toUserId, followedList);
		PageInfo pageInfo = new PageInfo(followedList);
		return pageInfo;
	}

	@Override
	public List<Long> followingListAllToUserId(Long fromUserId) {
		return userFollowDao.followingListAllToUserId(fromUserId);
	}

	@Override
	public List<Long> followedListAllFromUserId(Long toUserId) {
		return userFollowDao.followedListAllFromUserId(toUserId);
	}

}
